package criptografia;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.MessageDigest;

import javax.crypto.Cipher;
/**
 * Esse Objeto faz tanto criptografar quanto descriptografar baseado na chave armazenada no arquivo X1
 * Tambem pode ser utilizado para geracao e comparacao de MD5
 * No pacote de persistencia que teremos deve haver uma classe que use SealedObject e CipherOutputStream para gerar serializados seguros. 
 * @author Pedro Rawan Meireles Limeira em 11/09/2010
 * @version 0.1
 */
public class Encriptador {
	
	private Chave chave;//Um objeto chave que sera persistido
	private ObjectOutputStream saida;
	private OutputStream arquivoSaida;
	private ObjectInputStream entrada;
	private InputStream arquivoEntrada;
	private Cipher cipher; 
	
	
	private boolean verificaChaveExiste(){
		File arquivo = new File("X1.ser");
		return arquivo.exists();
		
	}
	/**
	 * O construtor do Encriptador ja faz uma verificao para ver se o arquivo X1 ja existe, por isso o programador
	 * Nao precisa se preocupar com esse detalhe
	 */
	public Encriptador(){
		if(!this.verificaChaveExiste()){
			/*Ja tratei as execessoes aqui porque so eh possivel acontecer
			  uma e ela ja eh tratada por esse if acima.
			*/
			try{
				this.chave = new Chave();
				arquivoSaida = new FileOutputStream("X1.ser");
				saida = new ObjectOutputStream(arquivoSaida);
				saida.writeObject(chave);
			}catch(Exception e){
				System.out.println("Erro na gravacao da chave");
			}
		}
		
		//Caso ja existe o arquivo com a chave
		else{
			try{
				/*
				 Ja tratei pelo menos motivo acima
				 */
				arquivoEntrada = new FileInputStream("X1.ser");
				entrada = new ObjectInputStream(arquivoEntrada);
				chave = (Chave) entrada.readObject();//Ler o objeto chave e faz um downcast
			}catch(Exception e){
				System.out.println("Erro na leitura da chave");
			}
		}
		try{
			cipher = Cipher.getInstance("AES");
		}catch(Exception e){
			System.out.println("Erro com o algoritimo");
		}
	}//FIm do construtor
	
	/**
	 * Criptografa um dado e gera um array de bytes com o dado criptografado
	 * @param dado - String
	 * @return byte[] - a ser gravado na persistencia
	 * @throws Exception 
	 */
	public byte[] encriptar(String dado) throws Exception{
		if(dado != null){
			cipher.init(Cipher.ENCRYPT_MODE, chave.getKey());
			return cipher.doFinal(dado.getBytes());
		}
		
		throw new Exception("A string nao pode ser nula");
	}
	
	/*public byte[] encriptar(Object dado) throws Exception{
		cipher.init(Cipher.DECRYPT_MODE, chave.getKey());
		//Pesquisar como fazer para gerar um array de bytes de object
	}*/
	/**
	 * 
	 */
	public byte[] encriptar(int dado) throws Exception{
		cipher.init(Cipher.ENCRYPT_MODE, chave.getKey());
		return cipher.doFinal(Integer.toString(dado).getBytes());
	}
	
	/**
	 * 
	 * @param dado - double
	 * @return byte[]
	 * @throws Exception
	 */
	public byte[] encriptar(double dado) throws Exception{
		cipher.init(Cipher.ENCRYPT_MODE, chave.getKey());
		return cipher.doFinal(Double.toString(dado).getBytes());
	}
	/**
	 * Descriptografa dados
	 * @param dado - byte[] pode ter sido lido de um arquivo ou mesmo da memoria
	 * @return String 
	 * @throws Exception
	 */
	public String decriptar(byte[] dado) throws Exception{
		cipher.init(Cipher.DECRYPT_MODE, chave.getKey());
		byte[] decipherText = cipher.doFinal(dado);
		String retorno = "";
		for(int i=0; i< decipherText.length; i++){
			retorno = retorno + String.valueOf(((char) decipherText[i]));
		}
		return retorno;
	}
	/**
	 * Gera um hash da String passada como parametro. Pode ser usado na comparacao e no armzanamento de senhas.
	 * @param dado - String
	 * @return String 
	 * @throws Exception
	 */
	public String geraHashParaSenha(String dado) throws Exception{
		MessageDigest digest = MessageDigest.getInstance("SHA-512");
		digest.update(dado.getBytes());
		String retorno="";
		byte[] arrayHash = digest.digest();
		for(int i=0; i < arrayHash.length; i++){
			retorno = retorno+((char) arrayHash[i]);
		}
		return retorno;
	}
	
	
}